package edu.whu.demo.Aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.*;

@Aspect
@Component
public class CountAspect {
    final Map<String, CountObject> records= Collections.synchronizedMap(new HashMap<>());

    private Logger logger = LoggerFactory.getLogger(getClass());
    // execution(* edu.whu.demo.controller.*(..)) 错误
    @Pointcut("execution(public * edu.whu.demo.controller.*.*(..))")
    public void pointCut(){

    }

    @Before("pointCut()")
    public void called(JoinPoint jp) {
        String name = jp.getSignature().toString();
        int times;
        CountObject record;
        if(records.containsKey(name)) {
            record = (CountObject) records.get(name);
            record.addCallCount();
            times = record.getCalledCount();
        }
        else {
            record = new CountObject();
            record.setCalledCount(1);
            record.setExceptionCount(0);
            record.setMaxTime(0);
            record.setMinTime(Long.MAX_VALUE);
            record.setMaxTime(0);
            record.setAvgTime(0.0);
            times = 1;
        }
        records.put(name, record);
        logger.info(name+"已被调用"+times+"次");
    }

    @Around("pointCut()")
    public Object updateTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String name = joinPoint.getSignature().toString();
        long t1 = Calendar.getInstance().getTimeInMillis();
        Object retVal;

            retVal = joinPoint.proceed(joinPoint.getArgs());


        long t2 = Calendar.getInstance().getTimeInMillis();
        long t = t2 - t1;
        long min,max; double avg;
        CountObject record;
        if(records.containsKey(name)){
            record = (CountObject) records.get(name);
            if(t > record.getMaxTime()){
                record.setMaxTime(t);
            }
            if(t < record.getMinTime()){
                record.setMinTime(t);
            }
            int count = record.getCalledCount() -  record.getExceptionCount();
            double total = record.getAvgTime() *
                    (count - 1) + t;
            record.setAvgTime(1.0 * total / count);
            avg = record.getAvgTime();
            min = record.getMinTime();
            max = record.getMaxTime();
        }
        else {
            record = new CountObject();
            record.setCalledCount(1);
            record.setExceptionCount(0);
            record.setAvgTime(t);
            record.setMaxTime(t);
            record.setMinTime(t);
            avg = t; min = t; max = t;
        }
        records.put(name, record);
        logger.info(name + " 方法最大调用时间: " + max +
                ", 最小调用时间: " + min + ", 平均调用时间: " + avg);
        return retVal;
    }


    @AfterThrowing(pointcut = "pointCut()", throwing = "e")
    public void ExceptionCount(JoinPoint jp, Exception e){
        String name = jp.getSignature().toString();
        CountObject record = (CountObject) records.get(name);
        record.addExceptionCount();
        logger.error("在"+name+"调用过程中触发"+e.toString()+"异常");
    }

    public Map<String, CountObject> getRecords() {
        return records;
    }
}

